Load the necessary libraries and source the utility functions.

library(survinng)
library(ggplot2)
library(cowplot)
library(dplyr)
library(tidyr)
library(SurvMetrics)
library(survival)
library(simsurv)
library(survminer)
library(torch)
library(viridis)
library(here)

# Load utility functions
source(here("utils/utils_nn_training.R"))
source(here("utils/utils_plotting.R"))


# Set figure path
fig_path <- here::here("figures_paper")
if (!file.exists(fig_path)) dir.create(fig_path)
fig <- function(x) here::here(fig_path, x)

Time-independent effects

Generate the data

Simulation setting: - \(10,000\) samples (\(9,500\) for training, \(500\) for testing) - No time-dependent effects - \(X_1 \sim \mathcal{N}(0,1)\) has a positive effect on the hazard -> negative effect on survival - \(X_2 \sim \mathcal{U}(0,1)\) has a stronger negative effect on the hazard -> positive effect on survival - \(X_3 \sim \mathcal{U}(-1,1)\) has no effect

set.seed(42)

# Simulate data
n <- 10000
x <- data.frame(x1 = rnorm(n), x2 = runif(n, 0, 1), x3 = runif(n, -1, 1))
simdat <- simsurv(dist = "weibull", lambdas = 0.1, gammas = 2.5, betas = c(x1 = 1.7, x2 = -2.4),
                  x = x, maxt = 7)
y <- simdat[, -1]
colnames(y)[1] <- "time"
dat <- cbind(y, x)

# Train/test
idx <- sample(n, 9500)
train <- dat[idx, ]
test <- dat[-idx, ]

Fit the models

ext_deephit <- fit_model("DeepHit", train, test)
ext_coxtime <- fit_model("CoxTime", train, test)
ext_deepsurv <- fit_model("DeepSurv", train, test)

Create Explainer

exp_deephit <- survinng::explain(ext_deephit[[1]], data = test)
exp_coxtime <- survinng::explain(ext_coxtime[[1]], data = test)
exp_deepsurv <- survinng::explain(ext_deepsurv[[1]], data = test)

Performance Measures

# Define a function to reshape data into a wide matrix format
prepare_matrix <- function(data, id_col = "id", time_col = "time", pred_col = "pred") {
  wide_data <- data %>%
    pivot_wider(names_from = {{time_col}}, values_from = {{pred_col}})
  
  # Convert the specified column to row names and remove it from the data
  wide_data <- as.data.frame(wide_data)
  rownames(wide_data) <- wide_data[[id_col]]
  wide_data <- wide_data[, -which(names(wide_data) == id_col)]
  
  # Convert to a matrix
  as.matrix(wide_data)
}

# Prepare matrices
matrix_coxtime <- prepare_matrix(ext_coxtime$pred)
matrix_deepsurv <- prepare_matrix(ext_deepsurv$pred)
matrix_deephit <- prepare_matrix(ext_deephit$pred)[,-1]

# Define survival object
surv_obj <- Surv(test$time, test$status)

# Define time indices and sampled time
t_interest <- sort(unique(ext_coxtime$pred$time))
num_samples <- 100
indices <- round(seq(1, length(t_interest), length.out = num_samples))
sampled_t <- t_interest[indices]
deephit_t <- sort(unique(ext_deephit$pred$time))[-1]

# Sample matrices
sampled_matrix_coxtime <- matrix_coxtime[, indices]
sampled_matrix_deepsurv <- matrix_deepsurv[, indices]

# Calculate Brier scores in a single step
calculate_brier <- function(matrix, times, surv_obj) {
  sapply(1:ncol(matrix), function(i) Brier(surv_obj, pre_sp = matrix[, i], times[i]))
}
metrics_coxtime <- calculate_brier(sampled_matrix_coxtime, sampled_t, surv_obj)
metrics_deepsurv <- calculate_brier(sampled_matrix_deepsurv, sampled_t, surv_obj)
metrics_deephit <- calculate_brier(matrix_deephit, deephit_t, surv_obj)

# Combine results into a single data frame for plotting
combine_results <- function(metrics, times, model_name) {
  data.frame(time = times, BS = metrics, model = model_name)
}
data_coxtime <- combine_results(metrics_coxtime, sampled_t, "CoxTime")
data_deepsurv <- combine_results(metrics_deepsurv, sampled_t, "DeepSurv")
data_deephit <- combine_results(metrics_deephit, deephit_t, "DeepHit")
data_BS <- rbind(data_coxtime, data_deepsurv, data_deephit)

# Plot Brier scores
colorblind_palette <- c("CoxTime" = "#E69F00", "DeepSurv" = "#56B4E9", "DeepHit" = "#009E73")
brier_plot_tid <- ggplot() +
  geom_line(data = data_BS, aes(x = time, y = BS, color = model, linetype = model)) +
  geom_rug(data = test, aes(x = time), sides = "bl", linewidth = 0.5, alpha = 0.5) +
  geom_hline(yintercept = 0) +
  scale_color_manual(values = colorblind_palette) +  # Apply custom colors
  scale_linetype_manual(values = c("CoxTime" = "solid", "DeepSurv" = "dashed", "DeepHit" = "dotted")) + 
  labs(title = "", x = "Time", y = "Brier Score", color = NULL, linetype = NULL) +
  theme_minimal(base_size = 17) +
  theme(legend.position = "bottom")
brier_plot_tid 


# Save plot
ggsave(fig("sim_tid_brier_plot.pdf"), plot = brier_plot_tid, width = 7, height = 5)

# Calculate C-index and IBS for each model
calculate_cindex <- function(matrix, surv_obj, index) {
  Cindex(surv_obj, predicted = matrix[, index])
}
calculate_ibs <- function(matrix, times, surv_obj) {
  IBS(surv_obj, sp_matrix = matrix, times)
}
C_coxtime <- calculate_cindex(sampled_matrix_coxtime, surv_obj, 50)
C_deepsurv <- calculate_cindex(sampled_matrix_deepsurv, surv_obj, 50)
C_deephit <- calculate_cindex(matrix_deephit, surv_obj, 15)
IBS_coxtime <- calculate_ibs(sampled_matrix_coxtime, sampled_t, surv_obj)
IBS_deepsurv <- calculate_ibs(sampled_matrix_deepsurv, sampled_t, surv_obj)
IBS_deephit <- calculate_ibs(matrix_deephit[,-1], deephit_t[-1], surv_obj)

# Display results
res <- data.frame(
  model = c("CoxTime", "DeepSurv", "DeepHit"),
  C_index = c(C_coxtime, C_deepsurv, C_deephit),
  IBS = c(IBS_coxtime, IBS_deepsurv, IBS_deephit)
)
saveRDS(res, fig("sim_tid_performance.rds"))
res
#>      model  C_index      IBS
#> 1  CoxTime 0.809372 0.099053
#> 2 DeepSurv 0.809121 0.099031
#> 3  DeepHit 0.808829 0.141614

Survival Prediction

# Print instances of interest
tid_ids <- c(13, 387)
print(test[tid_ids, ])
#>           time status        x1        x2          x3
#> 343  2.6653596      1 -0.434617 0.1162303 -0.08053765
#> 7906 0.9577924      1  2.454611 0.2462072 -0.04249294

# Compute Vanilla Gradient
grad_cox <- surv_grad(exp_coxtime, target = "survival", instance = tid_ids)
grad_deephit <- surv_grad(exp_deephit, target = "survival", instance = tid_ids)
grad_deepsurv <- surv_grad(exp_deepsurv, target = "survival", instance = tid_ids)

# Plot survival predictions
surv_plot <- cowplot::plot_grid(
  plot_surv_pred(grad_cox),
  plot_surv_pred(grad_deephit),
  plot_surv_pred(grad_deepsurv),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"),
  label_x = 0.03,      
  label_size = 14) 
surv_plot


# Save plot
ggsave(fig("sim_tid_surv_plot.pdf"), plot = surv_plot, width = 8, height = 14)

Explainable AI

Gradient (Sensitivity)

# Plot attributions
grad_plot <- cowplot::plot_grid(
  plot_attribution(grad_cox, label = "Grad(t)") ,
  plot_attribution(grad_deephit, label = "Grad(t)"),
  plot_attribution(grad_deepsurv, label = "Grad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
grad_plot


# Save plot
ggsave(fig("sim_tid_grad_plot.pdf"), plot = grad_plot, width = 10, height = 14)
# Plot normalized attributions
grad_plot_norm <- cowplot::plot_grid(
  plot_attribution(grad_cox, normalize = TRUE, label = "Grad(t)"),
  plot_attribution(grad_deephit, normalize = TRUE, label = "Grad(t)"),
  plot_attribution(grad_deepsurv, normalize = TRUE, label = "Grad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
grad_plot_norm


# Save plot
ggsave(fig("sim_tid_grad_plot_norm.pdf"), plot = grad_plot_norm, width = 10, height = 14)

SmoothGrad (Sensitivity)

# Compute SmoothGrad
sg_cox <- surv_smoothgrad(exp_coxtime, target = "survival", instance = tid_ids, n = 50, noise_level = 0.1)
sg_deephit <- surv_smoothgrad(exp_deephit, target = "survival", instance = tid_ids, n = 50, noise_level = 0.1)
sg_deepsurv <- surv_smoothgrad(exp_deepsurv, target = "survival", instance = tid_ids, n = 50, noise_level = 0.1)

# Plot attributions
smoothgrad_plot <- cowplot::plot_grid(
  plot_attribution(sg_cox, label = "SG(t)"), 
  plot_attribution(sg_deephit, label = "SG(t)"), 
  plot_attribution(sg_deepsurv, label = "SG(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
smoothgrad_plot


# Save plot
ggsave(fig("sim_tid_smoothgrad_plot.pdf"), plot = smoothgrad_plot, width = 10, height = 14)
# Plot normalized attributions
smoothgrad_plot_norm <- cowplot::plot_grid(
  plot_attribution(grad_cox, normalize = TRUE, label = "SG(t)"),
  plot_attribution(grad_deephit, normalize = TRUE, label = "SG(t)"),
  plot_attribution(grad_deepsurv, normalize = TRUE, label = "SG(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
smoothgrad_plot_norm


# Save plot
ggsave(fig("sim_tid_smoothgrad_plot_norm.pdf"), plot = smoothgrad_plot_norm, width = 10, height = 14)

Gradient x Input

# Compute GradientxInput
gradin_cox <- surv_grad(exp_coxtime, instance = tid_ids, times_input = TRUE)
gradin_deephit <- surv_grad(exp_deephit, instance = tid_ids, times_input = TRUE)
gradin_deepsurv <- surv_grad(exp_deepsurv, instance = tid_ids, times_input = TRUE)

# Plot attributions
gradin_plot <- cowplot::plot_grid(
  plot_attribution(gradin_cox, label = "GxI(t)"), 
  plot_attribution(gradin_deephit, label = "GxI(t)"), 
  plot_attribution(gradin_deepsurv, label = "GxI(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
gradin_plot


# Save plot
ggsave(fig("sim_tid_gradin_plot.pdf"), plot = gradin_plot, width = 10, height = 14)
# Plot attributions
grad_gradin_plot <- cowplot::plot_grid(
  plot_attribution(grad_deepsurv, label = "Grad(t)") ,
  plot_attribution(gradin_deepsurv, label = "GxI(t)"),
  nrow = 2, labels = c("DeepSurv", "DeepSurv"))
grad_gradin_plot


# Save plot
ggsave(fig("sim_tid_grad_gradin_plot.pdf"), plot = grad_gradin_plot, width = 10, height = 9)
# Plot attributions
gradin_plot_norm <- cowplot::plot_grid(
  plot_attribution(gradin_cox, normalize = TRUE, label = "GxI(t)"), 
  plot_attribution(gradin_deephit, normalize = TRUE, label = "GxI(t)"), 
  plot_attribution(gradin_deepsurv, normalize = TRUE, label = "GxI(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
gradin_plot_norm


# Save plot
ggsave(fig("sim_tid_gradin_plot_norm.pdf"), plot = gradin_plot_norm, width = 10, height = 14)

SmoothGrad x Input

# Compute SmoothGradxInput
sgin_cox <- surv_smoothgrad(exp_coxtime, instance = tid_ids, n = 50, noise_level = 0.3,
                          times_input = TRUE)
sgin_deephit <- surv_smoothgrad(exp_deephit, instance = tid_ids, n = 50, noise_level = 0.3,
                              times_input = TRUE)
sgin_deepsurv <- surv_smoothgrad(exp_deepsurv, instance = tid_ids, n = 50, noise_level = 0.3,
                               times_input = TRUE)

# Plot attributions
smoothgradin_plot <- cowplot::plot_grid(
  plot_attribution(sgin_cox, label = "SGxI(t)"), 
  plot_attribution(sgin_deephit, label = "SGxI(t)"), 
  plot_attribution(sgin_deepsurv, label = "SGxI(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
smoothgradin_plot


# Save plot
ggsave(fig("sim_tid_smoothgradin_plot.pdf"), plot = smoothgradin_plot, width = 10, height = 14)
# Plot attributions
smoothgradin_plot_norm <- cowplot::plot_grid(
  plot_attribution(sgin_cox, normalize = TRUE, label = "SGxI(t)"), 
  plot_attribution(sgin_deephit, normalize = TRUE, label = "SGxI(t)"), 
  plot_attribution(sgin_deepsurv, normalize = TRUE, label = "SGxI(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
smoothgradin_plot_norm


# Save plot
ggsave(fig("sim_tid_smoothgradin_plot_norm.pdf"), plot = smoothgradin_plot_norm, width = 10, height = 14)

IntegratedGradient

Zero baseline (should be proportional to Gradient x Input)

# Compute IntegratedGradient with 0 baseline
x_ref <- matrix(c(0,0,0), nrow = 1)
ig0_cox <- surv_intgrad(exp_coxtime, instance = tid_ids, n = 50, x_ref = x_ref)
ig0_deephit <- surv_intgrad(exp_deephit, instance = tid_ids, n = 50, x_ref = x_ref)
ig0_deepsurv <- surv_intgrad(exp_deepsurv, instance = tid_ids, n = 50, x_ref = x_ref)

# Plot attributions
intgrad0_plot <- cowplot::plot_grid(
  plot_attribution(ig0_cox, label = "IntGrad(t)"), 
  plot_attribution(ig0_deephit, label = "IntGrad(t)"), 
  plot_attribution(ig0_deepsurv, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgrad0_plot 


# Save plot
ggsave(fig("sim_tid_intgrad0_plot.pdf"), plot = intgrad0_plot, width = 10, height = 14)
# Plot attributions
intgrad0_plot_comp <- cowplot::plot_grid(
  plot_attribution(ig0_cox, add_comp = TRUE, label = "IntGrad(t)"), 
  plot_attribution(ig0_deephit, add_comp = TRUE, label = "IntGrad(t)"), 
  plot_attribution(ig0_deepsurv, add_comp = TRUE, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgrad0_plot_comp 


# Save plot
ggsave(fig("sim_tid_intgrad0_plot_comp.pdf"), plot = intgrad0_plot_comp, width = 10, height = 14)
# Plot contributions
intgrad0_plot_contr <- cowplot::plot_grid(
  plot_contribution(ig0_cox, label = "IntGrad(t)"), 
  plot_contribution(ig0_deephit, label = "IntGrad(t)"), 
  plot_contribution(ig0_deepsurv, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgrad0_plot_contr 


# Save plot
ggsave(fig("sim_tid_intgrad0_plot_contr.pdf"), plot = intgrad0_plot_contr, width = 10, height = 14)
# Plot force
intgrad0_plot_force <- cowplot::plot_grid(
  plot_force(ig0_cox, upper_distance = 0.04, lower_distance = 0.04, label = "IntGrad(t)"), 
  plot_force(ig0_deephit, upper_distance = 0.02, lower_distance = 0, label = "IntGrad(t)"), 
  plot_force(ig0_deepsurv, upper_distance = 0.04, lower_distance = 0.04, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgrad0_plot_force 


# Save plot
ggsave(fig("sim_tid_intgrad0_plot_force.pdf"), plot = intgrad0_plot_force, width = 10, height = 14)

Mean baseline

# Compute IntegratedGradient with mean baseline
x_ref <- NULL # default: feature-wise mean
igm_cox <- surv_intgrad(exp_coxtime, instance = tid_ids, n = 50, x_ref = x_ref)
igm_deephit <- surv_intgrad(exp_deephit, instance = tid_ids, n = 50, x_ref = x_ref)
igm_deepsurv <- surv_intgrad(exp_deepsurv, instance = tid_ids, n = 50, x_ref = x_ref)

# Plot attributions
intgradmean_plot <- cowplot::plot_grid(
  plot_attribution(igm_cox, label = "IntGrad(t)"), 
  plot_attribution(igm_deephit, label = "IntGrad(t)"), 
  plot_attribution(igm_deepsurv, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgradmean_plot


# Save plot
ggsave(fig("sim_tid_intgradmean_plot.pdf"), plot = intgradmean_plot, width = 10, height = 14)
# Plot attributions
intgradmean_plot_comp <- cowplot::plot_grid(
  plot_attribution(igm_cox, add_comp = TRUE, label = "IntGrad(t)"), 
  plot_attribution(igm_deephit, add_comp = TRUE, label = "IntGrad(t)"), 
  plot_attribution(igm_deepsurv, add_comp = TRUE, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgradmean_plot_comp


# Save plot
ggsave(fig("sim_tid_intgradmean_plot_comp.pdf"), plot = intgradmean_plot_comp, width = 10, height = 14)
# Plot contributions
intgradm_plot_contr <- cowplot::plot_grid(
  plot_contribution(igm_cox, label = "IntGrad(t)"), 
  plot_contribution(igm_deephit, label = "IntGrad(t)"), 
  plot_contribution(igm_deepsurv, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgradm_plot_contr 


# Save plot
ggsave(fig("sim_tid_intgradm_plot_contr.pdf"), plot = intgradm_plot_contr, width = 10, height = 14)
# Plot force
intgradm_plot_force <- cowplot::plot_grid(
  plot_force(igm_cox, upper_distance = 0, lower_distance = 0, label = "IntGrad(t)"), 
  plot_force(igm_deephit, upper_distance = 0, lower_distance = 0, label = "IntGrad(t)"), 
  plot_force(igm_deepsurv, upper_distance = 0, lower_distance = 0, label = "IntGrad(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
intgradm_plot_force


# Save plot
ggsave(fig("sim_tid_intgradm_plot_force.pdf"), plot = intgradm_plot_force, width = 10, height = 14)

GradShap

# Compute GradShap
gshap_cox <- surv_gradSHAP(exp_coxtime, instance = tid_ids, n = 50, num_samples = 100)
gshap_deephit <- surv_gradSHAP(exp_deephit, instance = tid_ids, n = 50, num_samples = 100)
gshap_deepsurv <- surv_gradSHAP(exp_deepsurv, instance = tid_ids, n = 50, num_samples = 100)

# Plot attributions
gshap_plot <- cowplot::plot_grid(
  plot_attribution(gshap_cox, label = "GradSHAP(t)"), 
  plot_attribution(gshap_deephit, label = "GradSHAP(t)"), 
  plot_attribution(gshap_deepsurv, label = "GradSHAP(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
gshap_plot 


# Save plot
ggsave(fig("sim_tid_gshap_plot.pdf"), plot = gshap_plot, width = 10, height = 14)
# Plot attributions
gshap_plot_comp <- cowplot::plot_grid(
  plot_attribution(gshap_cox, add_comp = TRUE, label = "GradSHAP(t)"), 
  plot_attribution(gshap_deephit, add_comp = TRUE, label = "GradSHAP(t)"), 
  plot_attribution(gshap_deepsurv, add_comp = TRUE, label = "GradSHAP(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
gshap_plot_comp


# Save plot
ggsave(fig("sim_tid_gshap_plot_comp.pdf"), plot = gshap_plot_comp, width = 10, height = 14)
# Plot contributions %
gshap_plot_contr <- cowplot::plot_grid(
  plot_contribution(gshap_cox, label = "GradSHAP(t)"), 
  plot_contribution(gshap_deephit, label = "GradSHAP(t)"), 
  plot_contribution(gshap_deepsurv, label = "GradSHAP(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
gshap_plot_contr 


# Save plot
ggsave(fig("sim_tid_gshap_plot_contr.pdf"), plot = gshap_plot_contr, width = 10, height = 14)
# Plot force
gshap_plot_force <- cowplot::plot_grid(
  plot_force(gshap_cox, upper_distance = 0, lower_distance = 0, lower_distance_x1 = 0.05, label = "GradSHAP(t)"), 
  plot_force(gshap_deephit, upper_distance = 0, lower_distance = 0, lower_distance_x1 = 0.02, label = "GradSHAP(t)"), 
  plot_force(gshap_deepsurv, upper_distance = 0, lower_distance = 0, lower_distance_x1 = 0.04, label = "GradSHAP(t)"),
  nrow = 3, labels = c("CoxTime", "DeepHit", "DeepSurv"))
gshap_plot_force


# Save plot
ggsave(fig("sim_tid_gshap_plot_force.pdf"), plot = gshap_plot_force, width = 10, height = 14)

Session Info

sessionInfo()
#> R version 4.4.3 (2025-02-28)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 22.04.5 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0 
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
#>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
#>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] batchtools_0.9.17      data.table_1.16.4      here_1.0.1            
#>  [4] callr_3.7.6            reticulate_1.40.0      torch_0.14.2          
#>  [7] tidyr_1.3.1            dplyr_1.1.4            viridis_0.6.5         
#> [10] viridisLite_0.4.2      cowplot_1.1.3          survivalmodels_0.1.191
#> [13] survex_1.2.0           survinng_0.1.0         SurvMetrics_0.5.0     
#> [16] survminer_0.5.0        ggpubr_0.6.0           ggplot2_3.5.2         
#> [19] survival_3.8-3         simsurv_1.0.0          devtools_2.4.5        
#> [22] usethis_3.1.0          rmarkdown_2.29        
#> 
#> loaded via a namespace (and not attached):
#>   [1] RColorBrewer_1.1-3    jsonlite_2.0.0        magrittr_2.0.3       
#>   [4] farver_2.1.2          DALEX_2.4.3           fs_1.6.5             
#>   [7] ragg_1.3.3            vctrs_0.6.5           memoise_2.0.1        
#>  [10] rstatix_0.7.2         htmltools_0.5.8.1     progress_1.2.3       
#>  [13] broom_1.0.7           Formula_1.2-5         sass_0.4.9           
#>  [16] parallelly_1.41.0     bslib_0.8.0           htmlwidgets_1.6.4    
#>  [19] zoo_1.8-12            cachem_1.1.0          mime_0.12            
#>  [22] lifecycle_1.0.4       iterators_1.0.14      pkgconfig_2.0.3      
#>  [25] Matrix_1.7-2          R6_2.6.1              fastmap_1.2.0        
#>  [28] future_1.34.0         shiny_1.10.0          digest_0.6.37        
#>  [31] numDeriv_2016.8-1.1   patchwork_1.3.0       ps_1.9.1             
#>  [34] rprojroot_2.0.4       pkgload_1.4.0         textshaping_1.0.0    
#>  [37] base64url_1.4         labeling_0.4.3        km.ci_0.5-6          
#>  [40] abind_1.4-8           compiler_4.4.3        remotes_2.5.0        
#>  [43] bit64_4.6.0-1         withr_3.0.2           brew_1.0-10          
#>  [46] backports_1.5.0       carData_3.0-5         pkgbuild_1.4.6       
#>  [49] ggsignif_0.6.4        MASS_7.3-65           lava_1.8.1           
#>  [52] rappdirs_0.3.3        sessioninfo_1.2.2     tools_4.4.3          
#>  [55] httpuv_1.6.15         future.apply_1.11.3   glue_1.8.0           
#>  [58] DiagrammeR_1.0.11     promises_1.3.2        grid_4.4.3           
#>  [61] checkmate_2.3.2       generics_0.1.4        gtable_0.3.6         
#>  [64] KMsurv_0.1-5          hms_1.1.3             car_3.1-3            
#>  [67] foreach_1.5.2         pillar_1.10.2         later_1.4.1          
#>  [70] splines_4.4.3         lattice_0.22-5        bit_4.6.0            
#>  [73] tidyselect_1.2.1      coro_1.1.0            miniUI_0.1.1.1       
#>  [76] knitr_1.49            gridExtra_2.3         xfun_0.50            
#>  [79] visNetwork_2.1.2      stringi_1.8.4         yaml_2.3.10          
#>  [82] pec_2023.04.12        evaluate_1.0.3        codetools_0.2-19     
#>  [85] data.tree_1.1.0       tibble_3.2.1          cli_3.6.5            
#>  [88] xtable_1.8-4          randomForestSRC_3.3.3 systemfonts_1.2.1    
#>  [91] processx_3.8.6        jquerylib_0.1.4       survMisc_0.5.6       
#>  [94] Rcpp_1.0.14           globals_0.16.3        png_0.1-8            
#>  [97] parallel_4.4.3        ellipsis_0.3.2        prettyunits_1.2.0    
#> [100] profvis_0.4.0         urlchecker_1.0.1      listenv_0.9.1        
#> [103] timereg_2.0.6         scales_1.4.0          prodlim_2024.06.25   
#> [106] purrr_1.0.2           crayon_1.5.3          rlang_1.1.6